﻿using System;
using System.Collections;
using System.Collections.Generic;

namespace NanoMvvm.Collections
{
    public class SortedList<T> : IList<T>
    {
        public SortedList()
            : this(Comparer<T>.Default)
        {
        }

        public SortedList(IComparer<T> comparer)
        {
            ListInternal = new List<T>();
            Comparer = comparer;
        }

        public int Count
        {
            get
            {
                return ListInternal.Count;
            }
        }

        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }

        protected List<T> ListInternal { get; private set; }

        protected IComparer<T> Comparer { get; private set; }

        public T this[int index]
        {
            get
            {
                return ListInternal[index];
            }

            set
            {
                if (index < 0 || index > ListInternal.Count)
                {
                    throw new ArgumentOutOfRangeException("index");
                }

                Insert(index, value);
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public IEnumerator<T> GetEnumerator()
        {
            return ListInternal.GetEnumerator();
        }

        public void Add(T item)
        {
            Insert(Count, item);
        }

        public void Clear()
        {
            OnClear();
        }

        public bool Contains(T item)
        {
            if (Comparer == null)
            {
                return ListInternal.Contains(item);
            }

            return ListInternal.BinarySearch(item, Comparer) >= 0;
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            ListInternal.CopyTo(array, arrayIndex);
        }

        public bool Remove(T item)
        {
            if (Comparer == null)
            {
                return ListInternal.Remove(item);
            }

            var index = ListInternal.BinarySearch(item, Comparer);

            if (index >= 0)
            {
                OnRemoveAt(index);
                return true;
            }

            return false;
        }

        public int IndexOf(T item)
        {
            if (Comparer == null)
            {
                return ListInternal.IndexOf(item);
            }

            var index = ListInternal.BinarySearch(item, Comparer);

            return index < -1 ? -1 : index;
        }

        public void Insert(int index, T item)
        {
            if (index < 0 || index > ListInternal.Count)
            {
                throw new ArgumentOutOfRangeException("index");
            }

            // determine index
            if (Comparer != null)
            {
                index = ListInternal.BinarySearch(item, Comparer);
            }

            if (index < 0)
            {
                // xor
                index = ~index;
            }

            OnInsertAt(index, item);
        }

        public void RemoveAt(int index)
        {
            if (index < 0 || index > ListInternal.Count)
            {
                throw new ArgumentOutOfRangeException("index");
            }

            OnRemoveAt(index);
        }

        protected virtual void OnInsertAt(int index, T item)
        {
            ListInternal.Insert(index, item);
        }

        protected virtual void OnRemoveAt(int index)
        {
            ListInternal.RemoveAt(index);
        }

        protected virtual void OnClear()
        {
            ListInternal.Clear();
        }
    }
}